/**
 **********************************************************************************************************************
 * @file    cot_os.c
 * @brief   该文件提供查询协作式多任务系统功能
 *
 * @details  功能详细说明：
 *           + 任务调度初始化
 *           + 任务调度功能启动
 *           + 该任务调度并非抢占式
 *
 * 使用方式:
 *    1. 使用前初始化函数 cotOs_Init
 *    2. 通过 cotOs_CreatTaskWithStack 或 cotOs_CreatTask 添加任务函数
 *    3. 主函数调用 cotOs_Start 启动任务调度, 存在任务函数时不会退出
 *    4. 使用 cotOs_Wait 切换到下一个任务执行
 *
 **********************************************************************************************************************
 */

/* Includes ----------------------------------------------------------------------------------------------------------*/

#include <stdbool.h>
#include "cot_os_config.h"
#include <setjmp.h>
#include "cot_os.h"

/* Private typedef ---------------------------------------------------------------------------------------------------*/

/**
 * @brief 系统支持的最大任务数量(独立栈任务数 + 共享栈任务数)
 */
#define COT_OS_MAX_TASK (COT_OS_MAX_UNIQUE_TASK + COT_OS_MAX_SHARED_TASK)

#if (COT_OS_MAX_TASK > 32)
#error "task max num can't over 32"
#endif

#if (COT_OS_MAX_TASK == 0)
#error "task min num can't less than 1"
#endif

/**
 * @brief 任务栈类型枚举
 */
typedef enum
{
    COT_OS_UNIQUE_STACK = 0, /* (0)独立栈模式，每个任务使用独立的栈空间 */
    COT_OS_SHARED_STACK      /* (1)共享栈模式，多个任务共用栈空间 */
} CotOSStackType_e;

/**
 * @brief 任务控制块结构体
 */
typedef struct stTCB
{
    char szName[COT_OS_TASK_NAME_LENGTH]; /* 任务名称 */
    uint16_t pid;                         /* 任务ID */
    uint8_t state;                        /* 任务状态 */
    uint8_t joinWaitCnt;                  /* 等待该任务结束的计数 */
    CotOSCondition_t cond;                /* 任务条件变量 */
    size_t stackTop;                      /* 任务栈顶地址 */
    size_t stackSize;                     /* 任务栈大小 */
    CotOSStackType_e eStackType;          /* 任务栈类型 */
    uint32_t nextRunTime;                 /* 下次运行时间 */
    int param;                            /* 任务参数 */
    jmp_buf env;                          /* 任务上下文 */
    cotOsTask_f pfnOsTaskEnter;           /* 任务入口函数 */
    CotOSCondition_t *pCondition;         /* 当前等待的条件变量 */
#if COT_OS_MAX_SHARED_TASK > 0
    uint8_t *pBakStack; /* 共享栈模式下的栈备份空间 */
    size_t stackUsed;   /* 共享栈模式下实际使用的栈大小 */
#endif
    struct stTCB *pNext; /* 指向下一个任务控制块 */
} TCB_t;

/**
 * @brief 系统信息结构体
 */
typedef struct stuOsInfo
{
    cotOsGetSysTime_f pfnGetTimerMs; /* 获取毫秒级别的时间回调函数 */
    TCB_t *pCurTCB;                  /* 当前运行的任务控制块 */
    TCB_t *pTCBList;                 /* 任务控制块链表 */
#if COT_OS_MAX_SHARED_TASK > 0
    uint32_t tcbStackMask; /* 共享栈使用位图 */
#endif
    uint32_t tcbMask;  /* 任务控制块使用位图 */
    jmp_buf env;       /* 主任务上下文 */
    jmp_buf envResume; /* 恢复任务上下文 */
    uint16_t pidCount; /* 任务ID计数器 */

    /* 下面定义的回调函数是防止编译器优化成内联函数使用，破坏压栈顺序导致无法正常运行 */
    void (*pfnRunTask)(uint32_t);              /* 运行任务函数指针 */
    size_t (*pfnGetStackTop)(void);            /* 获取栈顶地址函数指针 */
    size_t (*pfnCalculateStackUsage)(TCB_t *); /* 计算栈使用量函数指针 */
} OsInfo_t;

/* Private define ----------------------------------------------------------------------------------------------------*/

/* 任务状态标志宏 - 用于setjmp/longjmp任务切换过程 */
#define COMMON_TASK_INTI 0 /* 任务初始化状态 */
#define COMMON_TASK_RUN 1  /* 任务恢复运行状态 */

/* 主任务操作码 - 用于主任务上下文中的操作控制 */
#define MAIN_TASK_INTI 0             /* 主任务初始状态 */
#define MAIN_TASK_EXIT 1             /* 退出任务调度 */
#define MAIN_TASK_JUMP_SHARED_TASK 2 /* 跳转到共享栈任务 */
#define MAIN_TASK_START_TASK 3       /* 启动新任务 */

/* 任务生命周期状态 - 表示任务当前运行状态 */
#define TASK_STATUS_INIT 0    /* 任务已创建但未运行 */
#define TASK_STATUS_READY 1   /* 任务就绪等待执行 */
#define TASK_STATUS_RUNNING 2 /* 任务正在运行 */
#define TASK_STATUS_SUSPEND 3 /* 任务挂起等待事件或时间 */
#define TASK_STATUS_DELETED 4 /* 任务已结束待清理 */

/* 栈完整性检查值 - 用于检测任务栈是否被破坏 */
#define TCB_MAGIC 0xDEADBEEF

/* Private macro -----------------------------------------------------------------------------------------------------*/
/* Private variables -------------------------------------------------------------------------------------------------*/

static OsInfo_t sg_OsInfo;
static TCB_t sg_TCB[COT_OS_MAX_TASK];
#if COT_OS_MAX_SHARED_TASK > 0
static uint8_t sg_shareTCBStack[COT_OS_MAX_SHARED_TASK][COT_OS_MAX_SHARED_BAK_STACK_SIZE];
#endif

/* Private function prototypes ---------------------------------------------------------------------------------------*/
/* 内存和栈操作相关函数 */
static void MaxStrpy(char *out, const char *in, size_t maxLength);   /* 安全字符串拷贝函数 */
static void TcbMemcpy(uint8_t *pdest, uint8_t *psrc, size_t length); /* 任务栈内存拷贝函数 */
static size_t GetStackTop(void);                                     /* 获取当前栈顶地址 */
static size_t CalculateStackUsage(TCB_t *pTCB);                      /* 计算共享栈使用量 */
static void BackupSharedStack(TCB_t *pTCB);                          /* 备份共享栈内容 */

/* 任务控制块管理函数 */
static TCB_t *CreatTCB(OsInfo_t *pObj);                 /* 创建任务控制块 */
static void DestroyTCB(OsInfo_t *pObj, TCB_t *pCurTCB); /* 销毁任务控制块 */
#if COT_OS_MAX_SHARED_TASK > 0
static uint8_t *CreatTCBStack(OsInfo_t *pObj);               /* 创建共享栈备份空间 */
static void DestroyTCBStack(OsInfo_t *pObj, TCB_t *pCurTCB); /* 销毁共享栈备份空间 */
#endif

/* 任务链表管理函数 */
static void AddToTCBTaskList(OsInfo_t *pObj, TCB_t *pNewTCB);      /* 添加任务到任务链表 */
static void DeleteFromTCBTaskList(OsInfo_t *pObj, TCB_t *pCurTCB); /* 从任务链表中删除任务 */
static uint8_t GetTaskNum(OsInfo_t *pObj);                         /* 获取未删除的任务数量 */
static void DestoryTask(OsInfo_t *pObj, TCB_t *pCurTCB);           /* 清理已完成的任务 */

/* 任务调度与执行函数 */
static void RunTask(uint32_t check); /* 任务执行入口函数 */
#if COT_OS_MAX_SHARED_TASK > 0
static void StartSharedTask(void);         /* 启动共享栈任务 */
static void UseBuffer(volatile char *buf); /* 防编译优化辅助函数 */
#endif
static void JumpNextTask(OsInfo_t *pObj); /* 任务调度切换函数 */

/* Private function --------------------------------------------------------------------------------------------------*/
/**
 * @brief      字符串拷贝函数，带最大长度限制
 *
 * @param[out] out       目标字符串缓冲区
 * @param[in]  in        源字符串
 * @param[in]  maxLength 最大拷贝长度
 *
 * @note       该函数安全地拷贝字符串，并确保结果以'\0'结尾
 *             当源字符串长度超过maxLength-1时，只拷贝前maxLength-1个字符
 */
static void MaxStrpy(char *out, const char *in, size_t maxLength)
{
    /* 逐字符拷贝，直到遇到字符串结束符或达到最大长度 */
    while (*in != '\0' && maxLength > 1)
    {
        *out = *in;
        out++;
        in++;
        maxLength--;
    }

    /* 添加字符串结束符 */
    *out = '\0';
}

/**
 * @brief      内存拷贝函数，使用volatile防止编译器优化
 *
 * @param[out] pdest     目标内存地址
 * @param[in]  psrc      源内存地址
 * @param[in]  length    拷贝长度
 *
 * @note       此函数使用volatile修饰符确保内存操作不会被编译器优化掉
 *             对于任务栈内容的备份和恢复至关重要
 */
static void TcbMemcpy(uint8_t *pdest, uint8_t *psrc, size_t length)
{
    volatile uint8_t *d = (volatile uint8_t *)pdest;
    volatile uint8_t *s = (volatile uint8_t *)psrc;

    /* 逐字节拷贝 */
    while (length--)
    {
        *d++ = *s++;
    }
}

/**
 * @brief      获取当前栈顶地址
 *
 * @retval     返回16字节对齐的当前栈顶地址
 *
 * @note       函数通过获取局部变量的地址来确定当前栈位置
 *             为了保证内存对齐，返回值会按16字节边界对齐
 */
static size_t GetStackTop(void)
{
    volatile char stack_anchor;
    size_t stack_top = (size_t)&stack_anchor + sizeof(stack_anchor);
    return (stack_top + 15) & ~15UL; /* 16字节对齐 */
}

#if COT_OS_MAX_SHARED_TASK > 0
/**
 * @brief      计算任务实际使用的栈空间大小
 *
 * @param[in]  pTCB      任务控制块指针
 *
 * @retval     返回16字节对齐的栈使用量（字节数）
 *
 * @note       函数计算共享栈任务当前使用的栈空间大小
 *             会额外增加32字节安全区，并进行16字节对齐处理
 *             该数值用于任务栈内容的备份和恢复
 */
static size_t CalculateStackUsage(TCB_t *pTCB)
{
    volatile char anchor;
    size_t current_sp = (size_t)&anchor;
    size_t stack_used = pTCB->stackTop - current_sp + 32; /* 保留32字节安全区 */
    return (stack_used + 15) & ~15UL;                     /* 16字节对齐 */
}

/**
 * @brief      备份共享栈任务的栈内容
 *
 * @param[in]  pTCB      任务控制块指针
 *
 * @note       该函数在任务切换前将共享栈内容备份到任务的备份区域
 *
 *             如果程序卡在while循环，可能原因：
 *             - COT_OS_MAX_SHARED_BAK_STACK_SIZE 定义的值过小
 *             - 该共享栈任务定义了过多局部变量或有大型局部变量
 *             - 不按规定在嵌套函数中使用了cotOs_Wait或cotOs_ConditionWait函数
 */
static void BackupSharedStack(TCB_t *pTCB)
{
    pTCB->stackUsed = sg_OsInfo.pfnCalculateStackUsage(pTCB);
    /* 程序卡在这则说明有该共享栈任务使用的备份栈超过了设定值，说明有以下原因：
     * 1. COT_OS_MAX_SHARED_BAK_STACK_SIZE 定义的值过小
     * 2. 该共享栈任务定义了很多变量或者有个大内存变量
     * 3. 不按规定在嵌套函数中使用了`cotOs_Wait`或`cotOs_ConditionWait`函数
     */

    /* 检查栈使用量是否超过限制 */
    while (pTCB->stackUsed > COT_OS_MAX_SHARED_BAK_STACK_SIZE)
        ;

    /* 备份栈内容 */
    TcbMemcpy(pTCB->pBakStack, (uint8_t *)(pTCB->stackTop - pTCB->stackUsed), pTCB->stackUsed);
}

/**
 * @brief      恢复共享栈任务的栈内容
 *
 * @param[in]  pTCB      任务控制块指针
 *
 * @note       该函数在任务切换时将备份区域的栈内容恢复到共享栈
 *             如果程序卡在while循环，说明COT_OS_SHARED_TASK_JMP_STACK_SIZE定义的值过小
 *             需要增大该值以确保有足够的临时栈空间进行任务切换
 */
static void RestoreSharedStack(TCB_t *pTCB)
{
    /* 检查是否有足够的安全区，保留256字节安全区 */
    while (sg_OsInfo.pfnGetStackTop() - 256 < pTCB->stackTop)
        ; /* 卡在这里说明 COT_OS_SHARED_TASK_JMP_STACK_SIZE 定义的值过小 */

    /* 恢复栈内容 */
    TcbMemcpy((uint8_t *)(pTCB->stackTop - pTCB->stackUsed), pTCB->pBakStack, pTCB->stackUsed);
}
#endif

/**
 * @brief      创建任务控制块
 *
 * @param[in]  pObj      系统信息结构体指针
 *
 * @note       该函数从任务控制块数组中查找空闲位置并分配新的TCB
 *
 * @retval     非NULL    成功，返回任务控制块指针
 * @retval     NULL      失败，控制块已用尽
 */
static TCB_t *CreatTCB(OsInfo_t *pObj)
{
    /* 在任务控制块数组中查找空闲位置 */
    for (int i = 0; i < COT_OS_MAX_TASK; i++)
    {
        if (!((pObj->tcbMask >> i) & 0x01))
        {
            pObj->tcbMask |= (0x1 << i);
            return &sg_TCB[i];
        }
    }

    return NULL;
}

/**
 * @brief      销毁任务控制块
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pCurTCB   要销毁的任务控制块指针
 *
 * @note       该函数将任务控制块标记为未使用状态，清除使用标志位
 *             释放的TCB可在后续创建新任务时重复使用
 */
static void DestroyTCB(OsInfo_t *pObj, TCB_t *pCurTCB)
{
    /* 在任务控制块数组中找到对应位置并清除标记 */
    for (int i = 0; i < COT_OS_MAX_TASK; i++)
    {
        if (&sg_TCB[i] == pCurTCB)
        {
            pObj->tcbMask &= ~(0x1 << i);
            break;
        }
    }
}

#if COT_OS_MAX_SHARED_TASK > 0
/**
 * @brief      为共享栈任务分配栈备份空间
 *
 * @param[in]  pObj      系统信息结构体指针
 *
 * @note       该函数从预先定义的栈备份数组中查找空闲位置分配
 *
 * @retval     非NULL    成功，返回栈备份空间指针
 * @retval     NULL      失败，备份空间已用尽
 */
static uint8_t *CreatTCBStack(OsInfo_t *pObj)
{
    /* 在共享栈数组中查找空闲位置 */
    for (int i = 0; i < COT_OS_MAX_SHARED_TASK; i++)
    {
        if (!((pObj->tcbStackMask >> i) & 0x01))
        {
            pObj->tcbStackMask |= (0x1 << i);
            return sg_shareTCBStack[i];
        }
    }

    return NULL;
}

/**
 * @brief      释放共享栈任务的栈备份空间
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pCurTCB   要释放栈空间的任务控制块指针
 *
 * @note       该函数将栈备份空间标记为未使用状态，清除使用标志位
 *             释放的空间可在后续创建新的共享栈任务时重复使用
 */
static void DestroyTCBStack(OsInfo_t *pObj, TCB_t *pCurTCB)
{
    /* 在共享栈数组中找到对应位置并清除标记 */
    for (int i = 0; i < COT_OS_MAX_SHARED_TASK; i++)
    {
        if (sg_shareTCBStack[i] == pCurTCB->pBakStack)
        {
            pObj->tcbStackMask &= ~(0x1 << i);
            break;
        }
    }
}
#endif

/**
 * @brief      将任务控制块添加到任务链表
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pNewTCB   要添加的任务控制块指针
 *
 * @note       该函数将新建的任务控制块添加到循环链表中
 *             如果链表为空，则创建新的循环链表
 *             否则将新任务添加到链表尾部
 */
static void AddToTCBTaskList(OsInfo_t *pObj, TCB_t *pNewTCB)
{
    /* 如果链表为空，创建循环链表 */
    if (pObj->pTCBList == NULL)
    {
        pObj->pTCBList = pNewTCB;
        pObj->pTCBList->pNext = pObj->pTCBList;
    }
    else
    {
        /* 找到链表尾部并插入新节点 */
        TCB_t *pTCB = pObj->pTCBList;

        while (pTCB->pNext != NULL && pTCB->pNext != pObj->pTCBList)
        {
            pTCB = pTCB->pNext;
        }

        pNewTCB->pNext = pTCB->pNext;
        pTCB->pNext = pNewTCB;
    }
}

/**
 * @brief      从任务链表中删除指定任务
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pCurTCB   要删除的任务控制块指针
 *
 * @note       该函数从循环链表中移除指定的任务控制块
 *             如果链表中只有一个节点，则将链表设置为NULL
 *             如果删除的是头节点，会更新链表头指针
 */
static void DeleteFromTCBTaskList(OsInfo_t *pObj, TCB_t *pCurTCB)
{
    TCB_t *pTCB = pObj->pTCBList;

    /* 如果只有一个节点，直接清空链表 */
    if (pTCB->pNext == pObj->pTCBList)
    {
        pObj->pTCBList = NULL;
    }
    else
    {
        /* 找到要删除节点的前一个节点 */
        while (pTCB->pNext != pObj->pTCBList)
        {
            if (pTCB->pNext == pCurTCB)
            {
                break;
            }

            pTCB = pTCB->pNext;
        }

        /* 从链表中删除节点 */
        pTCB->pNext = pCurTCB->pNext;

        /* 如果删除的是头节点，更新链表头 */
        if (pCurTCB == pObj->pTCBList)
        {
            pObj->pTCBList = pCurTCB->pNext;
        }
    }
}

/**
 * @brief      获取当前未删除的任务数量
 *
 * @param[in]  pObj      系统信息结构体指针
 *
 * @retval     返回当前系统中未被标记为删除状态的任务总数
 *
 * @note       该函数遍历任务链表，统计所有状态不为DELETED的任务
 *             用于判断是否还有任务需要执行
 */
static uint8_t GetTaskNum(OsInfo_t *pObj)
{
    uint8_t num = 0;
    TCB_t *pTCB = sg_OsInfo.pTCBList;

    if (pTCB == NULL)
    {
        return 0;
    }

    /* 遍历循环链表，统计未删除的任务数 */
    do
    {
        if (pTCB->state != TASK_STATUS_DELETED)
        {
            num++;
        }

        pTCB = pTCB->pNext;
    } while (pTCB != sg_OsInfo.pTCBList);

    return num;
}

/**
 * @brief      初始化协程OS系统
 *
 * @param[in]  pfnOsTimer    获取毫秒级别的时间回调函数
 *
 * @note       该函数必须在使用任何其他协程OS函数前调用
 *             回调函数不可为NULL，否则系统将无法正常运行
 */
void cotOs_Init(cotOsGetSysTime_f pfnOsTimer)
{
    sg_OsInfo.pfnGetTimerMs = pfnOsTimer;
    sg_OsInfo.pTCBList = NULL;
    sg_OsInfo.pCurTCB = NULL;
#if COT_OS_MAX_SHARED_TASK > 0
    sg_OsInfo.tcbStackMask = 0;
#endif
    sg_OsInfo.tcbMask = 0;
    sg_OsInfo.pidCount = 0;
    sg_OsInfo.pfnRunTask = RunTask;
    sg_OsInfo.pfnGetStackTop = GetStackTop;
    sg_OsInfo.pfnCalculateStackUsage = CalculateStackUsage;
}

/**
 * @brief      销毁已完成的任务
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pCurTCB   要销毁的任务控制块指针
 *
 * @note       只有当任务状态为DELETED且没有其他任务等待它结束时才会被销毁
 *             销毁过程包括释放共享栈空间、从任务链表中移除和释放TCB
 */
static void DestoryTask(OsInfo_t *pObj, TCB_t *pCurTCB)
{
    if (pCurTCB->state == TASK_STATUS_DELETED && pCurTCB->joinWaitCnt == 0)
    {
#if COT_OS_MAX_SHARED_TASK > 0
        /* 释放共享栈空间 */
        DestroyTCBStack(&sg_OsInfo, pCurTCB);
#endif
        /* 从任务链表中删除 */
        DeleteFromTCBTaskList(&sg_OsInfo, pCurTCB);
        /* 释放任务控制块 */
        DestroyTCB(&sg_OsInfo, pCurTCB);
    }
}

/**
 * @brief      运行任务的入口函数
 *
 * @param[in]  check     任务栈完整性检查值，用于检测栈溢出
 *
 * @note       该函数是每个任务的真正入口点，主要职责：
 *             - 使能中断
 *             - 设置任务状态为运行中
 *             - 调用用户任务函数
 *             - 任务退出时进行资源清理
 *
 *             如果任务运行结束后check值不等于TCB_MAGIC，表明任务栈被破坏
 */
static void RunTask(uint32_t check)
{
    /* 使能中断 */
    COT_OS_ENABLE_IRQ();
    /* 设置任务状态为运行中 */
    sg_OsInfo.pCurTCB->state = TASK_STATUS_RUNNING;
    /* 调用任务入口函数 */
    sg_OsInfo.pCurTCB->pfnOsTaskEnter(sg_OsInfo.pCurTCB->param);
    /* 检查任务栈是否被破坏 */
    while (check != TCB_MAGIC)
        ; /* 程序卡在这则说明该任务的栈可能被破坏了 */
    /* 设置任务状态为已删除 */
    sg_OsInfo.pCurTCB->state = TASK_STATUS_DELETED;
    /* 通知等待该任务结束的其他任务 */
    cotOs_ConditionNotify(&sg_OsInfo.pCurTCB->cond);
    /* 尝试销毁任务 */
    DestoryTask(&sg_OsInfo, sg_OsInfo.pCurTCB);

    /* 如果还有其他任务，切换到下一个任务 */
    if (GetTaskNum(&sg_OsInfo) > 0)
    {
        JumpNextTask(&sg_OsInfo);
    }
    else
    {
        /* 所有任务都结束了，退出调度 */
        longjmp(sg_OsInfo.env, MAIN_TASK_EXIT);
    }
}

/**
 * @brief      检查新任务的栈空间是否与现有任务的栈空间重叠
 *
 * @param[in]  pObj      系统信息结构体指针
 * @param[in]  pStack    新任务的栈空间起始地址
 * @param[in]  stackSize 新任务的栈空间大小
 *
 * @note       该函数确保独立栈任务的栈空间不会与其他任务重叠
 *             防止多个任务共用同一栈区域导致的数据破坏
 *
 * @retval     true      栈空间不重叠，可以安全使用
 * @retval     false     栈空间存在重叠，不能使用
 */
static bool CheckStackMem(OsInfo_t *pObj, void *pStack, size_t stackSize)
{
    if (pObj->pTCBList != NULL)
    {
        TCB_t *pTCB = pObj->pTCBList;

        /* 计算新任务栈的范围 */
        size_t newStackTop = (size_t)pStack + stackSize;
        size_t newStackBase = (size_t)pStack;

        /* 遍历所有任务，检查栈空间是否重叠 */
        do
        {
            size_t stackTop = (size_t)pTCB->stackTop;
            size_t stackBase = (size_t)pTCB->stackTop - pTCB->stackSize;

            /* 检查是否有重叠区域 */
            if (!(newStackTop <= stackBase || newStackBase >= stackTop))
            {
                return false;
            }

            pTCB = pTCB->pNext;
        } while (pTCB != pObj->pTCBList);
    }

    return true;
}

/**
 * @brief      创建一个使用独立栈的任务
 *
 * @param[in]  pfnOsTaskEnter    任务入口函数
 * @param[in]  pName             任务名称，最大长度由COT_OS_TASK_NAME_LENGTH定义
 * @param[in]  pStack            任务栈空间起始地址，选择共享栈时该值为NULL
 * @param[in]  stackSize         栈空间大小(字节)，选择共享栈时该值为0
 * @param[in]  arg               传递给任务的参数
 *
 * @note       该函数创建一个具有独立栈空间的任务，每个任务都有自己的栈空间，互不干扰
 *             函数执行流程：
 *             - 检查系统是否已初始化
 *             - 验证栈空间参数的有效性
 *             - 检查栈空间是否与其他任务重叠
 *             - 分配和初始化任务控制块(TCB)
 *             - 将任务添加到任务链表中
 *
 * @retval     >=0               任务ID
 * @retval     COT_ERROR_NOT_INIT         系统未初始化
 * @retval     COT_ERROR_STACK_IS_NULL    栈空间参数无效
 * @retval     COT_ERROR_STACK_OVERLAPPING 栈空间与其他任务重叠
 * @retval     COT_ERROR_TCB_MEM_FAIL     TCB分配失败
 */
cotOsTask_t cotOs_CreatTaskWithStack(cotOsTask_f pfnOsTaskEnter, const char *pName, void *pStack, size_t stackSize, int arg)
{
    TCB_t *pNewTCB = NULL;

    /* 检查系统是否已初始化 */
    if (sg_OsInfo.pfnGetTimerMs == NULL)
    {
        return COT_ERROR_NOT_INIT;
    }

    /* 验证栈空间参数 */
    if (pStack == NULL || stackSize == 0)
    {
        return COT_ERROR_STACK_IS_NULL;
    }

    /* 检查栈空间是否与其他任务重叠 */
    if (!CheckStackMem(&sg_OsInfo, pStack, stackSize))
    {
        return COT_ERROR_STACK_OVERLAPPING;
    }

    /* 分配任务控制块 */
    pNewTCB = CreatTCB(&sg_OsInfo);

    if (NULL == pNewTCB)
    {
        return COT_ERROR_TCB_MEM_FAIL;
    }

    /* 初始化任务控制块 */
    MaxStrpy(pNewTCB->szName, pName, sizeof(pNewTCB->szName)); /* 设置任务名称 */
    pNewTCB->pid = sg_OsInfo.pidCount++;                       /* 分配任务ID */
    pNewTCB->pfnOsTaskEnter = pfnOsTaskEnter;                  /* 设置任务入口函数 */
    pNewTCB->param = arg;                                      /* 设置任务参数 */
    pNewTCB->stackTop = (size_t)pStack + stackSize;            /* 设置栈顶地址 */
    pNewTCB->stackSize = stackSize;                            /* 设置栈大小 */
    pNewTCB->eStackType = COT_OS_UNIQUE_STACK;                 /* 设置为独立栈模式 */
    pNewTCB->pNext = NULL;                                     /* 初始化链表指针 */
    pNewTCB->state = TASK_STATUS_INIT;                         /* 设置初始状态 */
    pNewTCB->pCondition = NULL;                                /* 初始化条件变量指针 */
#if COT_OS_MAX_SHARED_TASK > 0
    pNewTCB->pBakStack = NULL; /* 独立栈模式不需要备份栈 */
#endif
    pNewTCB->nextRunTime = 0;            /* 初始化下次运行时间 */
    pNewTCB->joinWaitCnt = 0;            /* 初始化等待计数 */
    cotOs_ConditionInit(&pNewTCB->cond); /* 初始化任务条件变量 */

    /* 将任务添加到任务链表 */
    AddToTCBTaskList(&sg_OsInfo, pNewTCB);

    return pNewTCB->pid;
}

/**
 * @brief      创建一个使用共享栈的任务
 *
 * @param[in]  pfnOsTaskEnter    任务入口函数
 * @param[in]  pName             任务名称，最大长度由COT_OS_TASK_NAME_LENGTH定义
 * @param[in]  arg               传递给任务的参数
 *
 * @attention  使用限制：
 *             1. cotOs_Wait和cotOs_ConditionWait函数只能在任务入口函数中直接调用，禁止在任务的嵌套函数中使用
 *             2. 任务入口函数中应避免定义过多局部变量或大内存变量，嵌套函数中的变量定义没有特殊限制
 *
 * @note       该函数创建一个使用共享栈空间的任务，共享栈任务共用同一个运行栈空间，可以节省系统内存资源
 *             函数执行流程：
 *             - 检查系统是否已初始化
 *             - 分配任务控制块(TCB)
 *             - 分配共享栈备份空间
 *             - 初始化任务相关参数
 *             - 将任务添加到任务链表
 *
 *             共享栈的工作原理：
 *             - 任务切换时会自动保存当前任务的栈内容
 *             - 切换到新任务时会恢复该任务的栈内容
 *             - 所有共享栈任务共用同一个运行栈空间
 *
 * @retval     >=0                         任务ID
 * @retval     COT_ERROR_NOT_INIT          系统未初始化
 * @retval     COT_ERROR_TCB_MEM_FAIL      TCB分配失败
 * @retval     COT_ERROR_TCB_MEM_STACK_FAIL 共享栈备份空间分配失败
 */
cotOsTask_t cotOs_CreatTask(cotOsTask_f pfnOsTaskEnter, const char *pName, int arg)
{
    TCB_t *pNewTCB = NULL;

    /* 检查系统是否已初始化 */
    if (sg_OsInfo.pfnGetTimerMs == NULL)
    {
        return COT_ERROR_NOT_INIT;
    }

    /* 分配任务控制块 */
    pNewTCB = CreatTCB(&sg_OsInfo);

    if (NULL == pNewTCB)
    {
        return COT_ERROR_TCB_MEM_FAIL;
    }

    /* 初始化任务控制块基本信息 */
    MaxStrpy(pNewTCB->szName, pName, sizeof(pNewTCB->szName)); /* 设置任务名称 */
    pNewTCB->pid = sg_OsInfo.pidCount++;                       /* 分配任务ID */
    pNewTCB->pfnOsTaskEnter = pfnOsTaskEnter;                  /* 设置任务入口函数 */
    pNewTCB->param = arg;                                      /* 设置任务参数 */

    /* 初始化共享栈相关参数 */
    pNewTCB->stackTop = 0;                     /* 共享栈模式下初始栈顶为0 */
    pNewTCB->eStackType = COT_OS_SHARED_STACK; /* 设置为共享栈模式 */
    /* 初始化任务控制块其他参数 */
    pNewTCB->pNext = NULL;             /* 初始化链表指针 */
    pNewTCB->state = TASK_STATUS_INIT; /* 设置初始状态 */
    pNewTCB->pCondition = NULL;        /* 初始化条件变量指针 */

#if COT_OS_MAX_SHARED_TASK > 0
    /* 为共享栈任务分配栈备份空间 */
    pNewTCB->pBakStack = CreatTCBStack(&sg_OsInfo); /* 分配栈备份空间 */

    if (pNewTCB->pBakStack == NULL)
    {
        DestroyTCB(&sg_OsInfo, sg_OsInfo.pCurTCB); /* 分配失败，释放TCB */
        return COT_ERROR_TCB_MEM_STACK_FAIL;
    }
#endif

    /* 初始化任务调度相关参数 */
    pNewTCB->nextRunTime = 0;            /* 初始化下次运行时间 */
    pNewTCB->joinWaitCnt = 0;            /* 初始化等待计数 */
    cotOs_ConditionInit(&pNewTCB->cond); /* 初始化任务条件变量 */

    /* 将任务添加到任务链表 */
    AddToTCBTaskList(&sg_OsInfo, pNewTCB);

    return pNewTCB->pid;
}

#if COT_OS_MAX_SHARED_TASK > 0
/**
 * @brief      启动共享栈任务
 *
 * @note       该函数负责初始化和启动一个共享栈任务，主要步骤：
 *             - 分配任务切换所需的临时栈空间
 *             - 记录当前栈顶位置
 *             - 保存任务上下文
 *             - 启动任务执行
 *
 *             实现细节：
 *             - 使用volatile修饰符防止编译器优化掉栈空间
 *             - 使用setjmp保存任务上下文，用于后续任务切换
 *             - TCB_MAGIC用于检测任务栈完整性
 */
static void StartSharedTask(void)
{
    /* 分配任务切换所需的临时栈空间 */
    volatile char resBuf[COT_OS_SHARED_TASK_JMP_STACK_SIZE];
    UseBuffer(resBuf); /* 防止编译器优化掉栈空间 */

    /* 记录当前任务的栈顶位置 */
    sg_OsInfo.pCurTCB->stackTop = sg_OsInfo.pfnGetStackTop();

    /* 保存任务上下文，并启动任务 */
    if (COMMON_TASK_INTI == setjmp(sg_OsInfo.pCurTCB->env))
    {
        sg_OsInfo.pfnRunTask(TCB_MAGIC); /* 启动任务，传入魔数用于检测栈完整性 */
    }
}

/**
 * @brief      防止编译器优化的辅助函数
 *
 * @param[in]  buf    需要操作的缓冲区指针
 *
 * @note       通过对传入缓冲区进行简单操作，防止编译器优化掉栈空间
 *             这对于保证共享栈任务的正常运行至关重要
 */
static void UseBuffer(volatile char *buf)
{
    /* 对缓冲区进行简单操作，防止编译优化 */
    buf[0] = 0;
}
#endif
/**
 * @brief      启动协程OS任务调度
 *
 * @note       该函数启动任务调度系统，会尝试运行第一个任务
 *             存在任务函数时该函数不会退出，因此一切准备就绪后最后调用该函数
 *
 * @retval     0     成功
 * @retval     -1    失败，可能的原因：系统未初始化或无可运行任务
 */
int cotOs_Start(void)
{
    if (sg_OsInfo.pTCBList == NULL || sg_OsInfo.pfnGetTimerMs == NULL)
    {
        return -1;
    }

    int ret = setjmp(sg_OsInfo.env);

    if (MAIN_TASK_INTI == ret)
    {
        sg_OsInfo.pCurTCB = sg_OsInfo.pTCBList;
        longjmp(sg_OsInfo.env, MAIN_TASK_START_TASK);
    }
    else if (MAIN_TASK_START_TASK == ret)
    {
        if (sg_OsInfo.pCurTCB->eStackType == COT_OS_UNIQUE_STACK)
        {
#if (COT_OS_MAX_UNIQUE_TASK > 0)
            COT_OS_SET_STACK(sg_OsInfo.pCurTCB->stackTop);
            sg_OsInfo.pfnRunTask(TCB_MAGIC);
#endif
        }
        else
        {
#if (COT_OS_MAX_SHARED_TASK > 0)
            StartSharedTask();
#endif
        }
    }
#if COT_OS_MAX_SHARED_TASK > 0
    else if (MAIN_TASK_JUMP_SHARED_TASK == ret)
    {
        RestoreSharedStack(sg_OsInfo.pCurTCB);
        longjmp(sg_OsInfo.pCurTCB->env, COMMON_TASK_RUN);
    }
#endif
    return 0;
}

/**
 * @brief      切换到下一个可运行的任务
 *
 * @param[in]  pObj      系统信息结构体指针
 *
 * @note       该函数实现任务调度的核心逻辑，按顺序执行以下操作：
 *             - 检查所有任务的运行条件(时间到期或条件变量触发)
 *             - 更新任务状态和条件变量
 *             - 按照循环顺序选择下一个可运行的任务
 *             - 根据任务类型(独立栈/共享栈)执行相应的上下文切换
 *
 *             该函数在无可运行任务时会一直循环等待，直到有任务就绪
 */
static void JumpNextTask(OsInfo_t *pObj)
{
    /* 检查任务链表是否为空 */
    if (pObj->pTCBList != NULL)
    {
        while (1)
        {
            /* 获取任务链表头 */
            TCB_t *pTCB = pObj->pTCBList;

            /* 第一次遍历：检查所有任务的运行条件 */
            do
            {
                /* 只检查未删除且已初始化的任务 */
                if (pTCB->state != TASK_STATUS_DELETED && pTCB->state != TASK_STATUS_INIT)
                {
                    /* 检查任务是否满足运行条件：
                     * 1. 等待时间已到期
                     * 2. 等待的条件变量已触发
                     */
                    if (((pTCB->nextRunTime != 0 && pObj->pfnGetTimerMs() > pTCB->nextRunTime) ||
                         (pTCB->pCondition != NULL && pTCB->pCondition->flag == 1)))
                    {
                        /* 将满足条件的任务设置为就绪状态 */
                        pTCB->state = TASK_STATUS_READY;
                    }
                }

                pTCB = pTCB->pNext;
            } while (pTCB != pObj->pTCBList);

            /* 第二次遍历：重置就绪任务的条件变量 */
            do
            {
                /* 如果任务就绪且有等待的条件变量，清除条件标志 */
                if (pTCB->state == TASK_STATUS_READY && pTCB->pCondition != NULL)
                    pTCB->pCondition->flag = 0;

                pTCB = pTCB->pNext;
            } while (pTCB != pObj->pTCBList);

            /* 从当前任务的下一个任务开始查找 */
            pTCB = pObj->pCurTCB->pNext;

            /* 第三次遍历：选择下一个可运行的任务并切换 */
            do
            {
                /* 如果找到就绪状态的任务 */
                if (pTCB->state == TASK_STATUS_READY)
                {
                    /* 更新当前任务指针和任务状态 */
                    pObj->pCurTCB = pTCB;
                    pObj->pCurTCB->state = TASK_STATUS_RUNNING;
#if COT_OS_MAX_SHARED_TASK > 0
                    /* 如果是共享栈任务，需要先恢复栈内容 */
                    if (pObj->pCurTCB->pBakStack != NULL)
                    {
                        COT_OS_DISABLE_IRQ();
                        /* 跳转到主任务上下文进行栈恢复 */
                        longjmp(sg_OsInfo.env, MAIN_TASK_JUMP_SHARED_TASK);
                    }
                    else
#endif
                    {
                        /* 独立栈任务直接切换上下文 */
                        longjmp(pObj->pCurTCB->env, COMMON_TASK_RUN);
                    }
                }
                /* 如果找到初始化状态的任务 */
                else if (pTCB->state == TASK_STATUS_INIT)
                {
                    /* 更新当前任务指针并禁止中断 */
                    sg_OsInfo.pCurTCB = pTCB;
                    COT_OS_DISABLE_IRQ();
                    /* 跳转到主任务上下文启动新任务 */
                    longjmp(sg_OsInfo.env, MAIN_TASK_START_TASK);
                }

                pTCB = pTCB->pNext;
            } while (pTCB != pObj->pCurTCB->pNext);
        }
    }
}

/**
 * @brief      任务阻塞等待指定时间
 *
 * @param[in]  time    等待时长，单位毫秒
 *
 * @note       该函数使当前任务进入阻塞状态，直到指定的等待时间结束
 *             任务会在大于等于指定时间后的某个时刻恢复运行，具体时间取决于其他任务何时让出执行权
 *
 *             实现细节：
 *             - 任务进入阻塞状态后，会立即让出执行权给其他任务
 *             - 如果是共享栈任务，会先备份当前栈内容
 *             - 实际等待时间可能会大于指定时间，因为要等待其他任务让出执行权
 */
void cotOs_Wait(uint32_t time)
{
    /* 计算任务下次运行时间 */
    sg_OsInfo.pCurTCB->nextRunTime = sg_OsInfo.pfnGetTimerMs() + time;
    /* 清除条件变量等待标志 */
    sg_OsInfo.pCurTCB->pCondition = NULL;
    /* 设置任务状态为挂起 */
    sg_OsInfo.pCurTCB->state = TASK_STATUS_SUSPEND;

    /* 保存当前任务上下文，如果不是从恢复点返回，则进行任务切换 */
    if (COMMON_TASK_RUN != setjmp(sg_OsInfo.pCurTCB->env))
    {
#if COT_OS_MAX_SHARED_TASK > 0
        /* 如果是共享栈任务，需要备份当前栈内容 */
        if (sg_OsInfo.pCurTCB->pBakStack != NULL)
        {
            BackupSharedStack(sg_OsInfo.pCurTCB);
        }
#endif
        /* 跳转到下一个可运行的任务 */
        JumpNextTask(&sg_OsInfo);
    }

    /* 恢复运行后重新使能中断 */
    COT_OS_ENABLE_IRQ();
}

/**
 * @brief      等待指定任务结束
 *
 * @param[in]  task    要等待的任务ID，由cotOs_CreatTask或cotOs_CreatTaskWithStack返回
 *
 * @note       该函数使当前任务进入阻塞状态，直到指定的任务结束执行
 *             当目标任务结束后，会自动清理其资源并从任务列表中移除
 *
 *             使用说明：
 *             - 如果指定的任务不存在或已经结束，函数会直接返回
 *             - 多个任务可以同时等待同一个任务结束
 *             - 任务结束后会自动释放其占用的资源
 */
void cotOs_Join(cotOsTask_t task)
{
    TCB_t *pTCB = sg_OsInfo.pTCBList;

    do
    {
        /* 查找指定ID的任务，且该任务未被删除 */
        if (pTCB->pid == task && pTCB->state != TASK_STATUS_DELETED)
        {
            /* 增加等待计数，表示有一个任务在等待它结束 */
            pTCB->joinWaitCnt++;
            /* 等待任务结束时发出的条件变量通知 */
            cotOs_ConditionWait(&pTCB->cond);
            /* 等待结束后，减少等待计数 */
            pTCB->joinWaitCnt--;
            /* 尝试销毁任务（如果没有其他任务等待它） */
            DestoryTask(&sg_OsInfo, pTCB);
            break;
        }

        pTCB = pTCB->pNext;
    } while (pTCB != sg_OsInfo.pTCBList);
}

/**
 * @brief      获取当前正在运行的任务ID
 *
 * @retval     返回当前任务的唯一标识符
 *
 * @note       该函数返回当前正在执行的任务的唯一标识符
 *             每个任务在创建时都会被分配一个唯一的ID，可用于任务的识别和管理
 *
 *             ID特性：
 *             - 任务ID是在任务创建时自动分配的
 *             - ID值从0开始递增
 *             - 即使任务结束，其ID也不会被重复使用
 */
uint16_t cotOs_Pid(void)
{
    /* 直接返回当前运行任务的ID */
    return sg_OsInfo.pCurTCB->pid;
}

/**
 * @brief      初始化条件变量
 *
 * @param[out] pCondition    指向要初始化的条件变量的指针
 *
 * @note       该函数用于初始化一个条件变量，将其状态设置为未触发状态
 *             条件变量用于任务间的同步，一个任务可以等待条件变量被其他任务触发
 *
 *             使用注意事项：
 *             - 使用条件变量前必须先调用此函数进行初始化
 *             - 如果传入的指针为NULL，函数会直接返回
 *             - 同一个条件变量可以被多个任务等待
 *             - 条件变量的内存空间必须在调用此函数前分配好
 */
void cotOs_ConditionInit(CotOSCondition_t *pCondition)
{
    /* 参数有效性检查 */
    if (pCondition == NULL)
    {
        return;
    }

    /* 初始化条件变量标志为未触发状态 */
    pCondition->flag = 0;
}

/**
 * @brief      任务阻塞等待条件变量被触发
 *
 * @param[in]  pCondition    指向要等待的条件变量的指针
 *
 * @note       该函数使当前任务进入阻塞状态，直到指定的条件变量被触发
 *             当条件变量被其他任务通过cotOs_ConditionNotify触发后，任务会在某个时刻恢复运行
 *
 *             使用注意事项：
 *             - 如果传入的指针为NULL，函数会直接返回
 *             - 任务进入阻塞状态后，会立即让出执行权给其他任务
 *             - 如果是共享栈任务，会先备份当前栈内容
 *             - 实际恢复运行的时间取决于其他任务何时让出执行权
 *             - 条件变量必须先通过cotOs_ConditionInit初始化
 *             - 多个任务可以同时等待同一个条件变量
 */
void cotOs_ConditionWait(CotOSCondition_t *pCondition)
{
    /* 参数有效性检查 */
    if (pCondition == NULL)
    {
        return;
    }

    /* 设置任务下次运行时间为0，表示可以立即运行 */
    sg_OsInfo.pCurTCB->nextRunTime = 0;
    /* 记录当前任务正在等待的条件变量 */
    sg_OsInfo.pCurTCB->pCondition = pCondition;
    /* 将任务状态设置为挂起 */
    sg_OsInfo.pCurTCB->state = TASK_STATUS_SUSPEND;

    /* 保存当前任务上下文，如果不是从恢复点返回，则进行任务切换 */
    if (COMMON_TASK_RUN != setjmp(sg_OsInfo.pCurTCB->env))
    {
#if COT_OS_MAX_SHARED_TASK > 0
        /* 如果是共享栈任务，需要先备份当前栈内容 */
        if (sg_OsInfo.pCurTCB->pBakStack != NULL)
        {
            BackupSharedStack(sg_OsInfo.pCurTCB);
        }
#endif
        /* 切换到下一个可运行的任务 */
        JumpNextTask(&sg_OsInfo);
    }

    /* 任务恢复运行后，重新使能中断 */
    COT_OS_ENABLE_IRQ();
}

/**
 * @brief      触发一个条件变量
 *
 * @param[in]  pCondition    指向要触发的条件变量的指针
 *
 * @note       该函数用于触发一个条件变量，使等待该条件变量的任务可以恢复运行
 *             当条件变量被触发后，所有等待该条件变量的任务都将被唤醒
 *
 *             使用注意事项：
 *             - 如果传入的指针为NULL，函数会直接返回
 *             - 触发条件变量后，等待该条件变量的任务不会立即运行，而是进入就绪状态
 *             - 实际的任务恢复运行顺序取决于调度器的调度策略
 *             - 如果没有任务等待该条件变量，触发操作不会产生任何效果
 *             - 条件变量必须先通过cotOs_ConditionInit初始化才能使用
 */
void cotOs_ConditionNotify(CotOSCondition_t *pCondition)
{
    /* 参数有效性检查 */
    if (pCondition == NULL)
    {
        return;
    }

    /* 设置条件变量标志为已触发状态 */
    pCondition->flag = 1;
}

#if 0
#include <stdio.h>

#define GET_STATUS(state) (state) == TASK_STATUS_READY ? "R" : ((state) == TASK_STATUS_RUNNING ? "X" : ((state) == TASK_STATUS_SUSPEND ? "S" : "D"))

void cotOs_Top(void)
{
    printf("%3s  %-16s %-6s %-8s\n", "pid", "name", "state", "stack");

    for (int i = 0; i < COT_OS_MAX_TASK; i++)
    {
        if (((sg_OsInfo.tcbMask >> i) & 0x01))
        {
            printf("%3d  %-16s %-6s %-8lx\n", sg_TCB[i].pid, sg_TCB[i].szName, GET_STATUS(sg_TCB[i].state), sg_TCB[i].stackTop);
        }
    }
}
#endif
